
#include <cmath>
#include <vector>

#include "ddc_main.h"
#include "ddc_obj.h"

DDC_FRect DDC_RectToFRect(DDC_Rect *rect) {
    return DDC_FRect {(float)rect->x, (float)rect->y,
                      (float)rect->width, (float)rect->height};
}

DDC_Rect DDC_FRectToRect(DDC_FRect *rect) {
    return DDC_Rect {(int)rect->x, (int)rect->y,
                     (int)rect->width, (int)rect->height};
}

// class DDC_Vector2 {

DDC_Point2D DDC_Vector2::ToIntPoint() {
    return DDC_Point2D{.x = (int)x, .y = (int)y};
}

// } DDC_Vector2

// class DDC_GameObject {

std::vector<DDC_Behavior*>& DDC_GameObject::GetBehaviors() {
    return this->_behaviors;
}

std::vector<DDC_GameObject*>& DDC_GameObject::GetSubObjects() {
    return this->_subObjects;
}

std::vector<DDC_Component*>& DDC_GameObject::GetComponents() {
    return this->_components;
}

DDC_GameObject *DDC_GameObject::NewSubGameObject(std::string name, int *outIndex) {
    *outIndex = _subObjects.size();

    DDC_GameObject *gameObject = new DDC_GameObject(name);
    _subObjects.push_back(gameObject);

    return gameObject;
}

DDC_GameObject *DDC_GameObject::NewSubGameObject(std::string name) {
    int _;
    return DDC_GameObject::NewSubGameObject(name, &_);
}

DDC_GameObject::~DDC_GameObject() {
    for (DDC_Behavior *bh: _behaviors) {
        delete bh;
    }

    for (DDC_GameObject *go: _subObjects) {
        delete go;
    }

    for (DDC_Component *cp: _components) {
        delete cp;
    }
}

// } DDC_GameObject

// DDC_Scene {

DDC_GameObject *DDC_Scene::NewGameObject(std::string name, int *outIndex) {
    *outIndex = _gameObjects.size();

    DDC_GameObject *gameObject = new DDC_GameObject(name);
    _gameObjects.push_back(gameObject);

    gameObject->__AttachScene(this);

    return gameObject;
}

DDC_GameObject *DDC_Scene::NewGameObject(std::string name) {
    int _;
    return DDC_Scene::NewGameObject(name, &_);
}

/**
 * :return: > 0 if error occured.
 */
bool DDC_Scene::RemoveGameObject(int index, bool deleteIt) {
    if (index < 0 || index >= _gameObjects.size())
        return 1;

    if (deleteIt)
        delete _gameObjects[index];

    _gameObjects.erase(_gameObjects.begin() + index);

    return 0;
}

DDC_GameObject *DDC_Scene::GetGameObejct(int index) {
    if (index < 0 || index >= _gameObjects.size())
        return NULL;

    return _gameObjects[index];
}

DDC_GameObject *DDC_Scene::GetGameObejct(std::string name) {
    for (DDC_GameObject *gop: _gameObjects) {
        if (gop->name == name)
            return gop;
    }
    return NULL;
}

void DDC_Scene::OnStart() {
    DDC_GameObject *gameObject;
    int size = _gameObjects.size();

    for (int i = 0; i < size; ++i) {
        gameObject = _gameObjects[i];

        for (DDC_Behavior* bh: gameObject->GetBehaviors()) {
            bh->OnStart();
        }
    }
}

void DDC_Scene::Update() {
    DDC_GameObject *gameObject;
    int size = _gameObjects.size();

    for (int i = 0; i < size; ++i) {
        gameObject = _gameObjects[i];

        for (DDC_Component *cp: gameObject->GetComponents()) {
            cp->Update();
        }

        for (DDC_Behavior *bh: gameObject->GetBehaviors()) {
            bh->Update();
        }
    }
}

DDC_Scene::DDC_Scene(std::string name)
    :name(name) {

    int w, h;
    Dodoco::GetDodoco()->GetWindowSize(&w, &h);

    _worldCamera = this->NewGameObject("MainCamera");
    _worldCamera->gameObjectRect.x = 0;
    _worldCamera->gameObjectRect.y = 0;
    _worldCamera->gameObjectRect.height = h;
    _worldCamera->gameObjectRect.width = w;
}

void DDC_Scene::WorldTransformToCamera(const DDC_FRect *goRect,
                                       const double goRotAngle,
                                       const DDC_Vector2 *goAnchor,
                                       DDC_FRect *outRect,
                                       double *outRotAngle,
                                       DDC_Vector2 *outAnchor) {
    double camRotAngle = _worldCamera->rotationAngle;
    DDC_Vector2 camRotAnchor = _worldCamera->anchor;
    DDC_FRect *camRect = &_worldCamera->gameObjectRect;

    outRect->x = goRect->x - camRect->x;
    outRect->y = goRect->y - camRect->y;
    outRect->height = goRect->height;
    outRect->width = goRect->width;

    *outRotAngle = goRotAngle - camRotAngle;
    outAnchor->x = camRect->width * camRotAnchor.x;
    outAnchor->y = camRect->height * camRotAnchor.y;

    double length = sqrt(pow(goAnchor->x - camRotAnchor.x, 2) +
                         pow(goAnchor->y - camRotAnchor.y, 2));

    if (length != 0) {
        double _angle = acos(outRect->x / length) - camRotAngle;
        outAnchor->x = length * cos(_angle);
        outAnchor->y = length * sin(_angle);
    }
}

// } DDC_Scene
